package org.joe_e.eclipse;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.TreeMap;
import java.util.Map;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.joe_e.eclipse.Taming.Entry;

public class PolicyWriter {
    static void write(Taming taming, IFile policyFile) {
        Map<IType, Entry> db = taming.db;
        ByteArrayOutputStream content = new ByteArrayOutputStream();
        PrintStream out = new PrintStream(content);
        out.println("// This file is auto-generated by the Joe-E builder " +
                    "based on the taming");
        out.println("// database (safej files), and should not be edited " +
                    "directly.");
        out.println("package org.joe_e.taming;");
        out.println();
        out.println("import java.util.HashMap;");        
        out.println("import java.util.HashSet;");
        //out.println("import org.joe_e.*;");
        out.println();        
        out.println("public class Policy {");
        out.println("    private Policy() {}");
        out.println();
        out.println("    private static HashMap<String, String[]> honoraries = ");       
        out.println("        new HashMap<String, String[]>();");      
        out.println("    private static HashSet<String> fields = " 
                    + "new HashSet<String>();");
        out.println("    private static HashSet<String> constructors = "
                    + "new HashSet<String>();");
        out.println("    private static HashSet<String> methods = "
                    + "new HashSet<String>();");
        out.println();
        out.println("    public static boolean hasHonorary(String type, " +
                                                          "String honorary) {");
        out.println("        if (honoraries.containsKey(type)) {");
        out.println("            for (String hon : honoraries.get(type)) {");
        out.println("                if (hon.equals(honorary)) {");
        out.println("                    return true;");
        out.println("                }");
        out.println("            }");
        out.println("        }");
        out.println("        return false;");
        out.println("    }");
        out.println();
        out.println("    public static boolean fieldEnabled(String " + 
                                                           "fieldSig) {");
        out.println("        return fields.contains(fieldSig);");
        out.println("    }");
        out.println();
        out.println("    public static boolean constructorEnabled(String " +
                                                                 "ctorSig) {");
        out.println("        return constructors.contains(ctorSig);");
        out.println("    }");
        out.println();
        out.println("    public static boolean methodEnabled(String " +
                                                            "methodSig) {");
        out.println("        return methods.contains(methodSig);");
        out.println("    }");
        out.println();
        out.println("    static {");        
        
        TreeMap<String, Entry> sortedDB = new TreeMap<String, Taming.Entry>();
        for (IType type : db.keySet()) {
            sortedDB.put(type.getFullyQualifiedName(), db.get(type));
        }
        
        boolean firstType = true;
        for (String fqn : sortedDB.keySet()) {
            Entry e = sortedDB.get(fqn);
            
            out.println((firstType ? "" : "\n") + "        // Type " + fqn);
            firstType = false;
            
            List<IType> honoraries = taming.detag(e.honoraries);
            if (honoraries != null && !honoraries.isEmpty()) {
                //out.print("        honoraries.add(" + fqn + 
                //          ".class, new Class<?>[]{");
                out.print("        honoraries.put(\"" + fqn + 
                          "\", new String[]{");
                boolean firstHon = true;
                for (IType hon : honoraries) {
                    out.print(firstHon ? "" : ", ");
                    firstHon = false;
                    out.print("\"" + hon.getFullyQualifiedName() + "\"");
                }
                out.println("});");
            }
            
            Map<IField, String> fields = e.allowedFields;
            if (fields != null) {
                for (IField f : fields.keySet()) {
                    out.println("        fields.add(\"" + fqn + "." + 
                                f.getElementName() + "\");");
                }
            }
            
            Map<IMethod, String> methods = e.allowedMethods;
            if (methods != null) {
                for (IMethod m : methods.keySet()) {
                    try {
                        if (m == null || m.isConstructor()) {
                            String flatSig = "()";
                            if (m != null) {
                                flatSig = Taming.getFlatSignature(m);
                                flatSig = flatSig.substring(flatSig.indexOf('('));
                            }
                            out.println("        constructors.add(\"" + fqn +
                                        flatSig + "\");");
                        }
                    } catch (JavaModelException jme) {
                        jme.printStackTrace(System.err);
                        return;
                    }
                }
                for (IMethod m : methods.keySet()) {
                    try {
                        if (m != null && !m.isConstructor()) {
                            out.println("        methods.add(\"" + fqn + 
                                        "." + Taming.getFlatSignature(m) + "\");");
                        }
                    } catch (JavaModelException jme) {
                        jme.printStackTrace(System.err);
                        return;
                    }
                }
            }
        }
        
        out.println("    }"); // end static {
        out.println("}"); // end class {
        
        ByteArrayInputStream stream = new ByteArrayInputStream(content.toByteArray());
        try {
            if (policyFile.exists()) {
                policyFile.setContents(stream, false, true, null);
            } else {
                policyFile.create(stream, false, null);
            }
            policyFile.setDerived(true);
        } catch (CoreException ce) {
            System.err.println("couldn't write policy output file!");
            ce.printStackTrace(System.err);
        }
    }
}
